﻿using System;
using NUnit.Framework;

namespace NSecurity.UnitTests
{
    // ReSharper disable InconsistentNaming
    [TestFixture]
    public class NSecurityUnitTests
    {
        private AccessControlService<string, string, string> service;

        [SetUp]
        public void NSecurityUnitTests_SetUp()
        {
            this.service.ClearAccessControlEntries();

        }

        [TestFixtureSetUp]
        public void TestFixtureSetUp()
        {
            this.service = new AccessControlService<string, string, string> { PathDelimiter = "." };

            this.service.GetAncestor = s =>
            {
                var parts = s.Split(new[] { this.service.PathDelimiter }, StringSplitOptions.None);
                var result = string.Join(this.service.PathDelimiter, parts, 0, parts.Length - 1);
                return result == string.Empty ? null : result;
            };

            this.service.GetRoles = s => new[] { "roleA", "roleB" };
        }

        [Test]
        public void Add_Allow_User()
        {
            const string securable = "r1";
            const string verb = "v1";
            const string subject = "s1";

            // Act
            this.service.Allow(securable, verb, subject);
            var result = this.service.IsAllowed(securable, verb, subject);

            // Assert
            Assert.That(result, Is.True);
        }

        [Test]
        public void Add_Deny_User()
        {
            const string securable = "r1";
            const string verb = "v1";
            const string subject = "s1";

            // Act
            this.service.Deny(securable, verb, subject);
            var result = this.service.IsAllowed(securable, verb, subject);

            // Assert
            Assert.That(result, Is.False);
        }

        [Test]
        public void Add_Deny_After_Allow_User_Should_Deny()
        {
            const string securable = "r1";
            const string verb = "v1";
            const string subject = "s1";

            // Act
            this.service.Allow(securable, verb, subject);
            this.service.Deny(securable, verb, subject);
            var result = this.service.IsAllowed(securable, verb, subject);

            // Assert
            Assert.That(result, Is.False);
        }

        [Test]
        public void Add_Allow_After_Deny_User_Should_Not_Allow()
        {
            const string securable = "r1";
            const string verb = "v1";
            const string subject = "s1";

            // Act
            this.service.Deny(securable, verb, subject);
            this.service.Allow(securable, verb, subject);
            var result = this.service.IsAllowed(securable, verb, subject);

            // Assert
            Assert.That(result, Is.False);
        }

        [Test]
        public void Add_Allow_To_A_Parent_Securable()
        {
            const string parent = "r1";
            var securable = parent + this.service.PathDelimiter + "r1";
            const string verb = "v1";
            const string subject = "s1";

            // Act
            this.service.Allow(parent, verb, subject);
            var result = this.service.IsAllowed(securable, verb, subject);

            // Assert
            Assert.That(result, Is.True);
        }

        [Test]
        public void Add_Allow_To_A_Securable_And_Deny_To_A_Parent_Securable()
        {
            const string parent = "r1";
            var securable = parent + this.service.PathDelimiter + "r1";
            const string verb = "v1";
            const string subject = "s1";

            // Act
            this.service.Allow(securable, verb, subject);
            this.service.Deny(parent, verb, subject);
            var result = this.service.IsAllowed(securable, verb, subject);

            // Assert
            Assert.That(result, Is.True);
        }

        [Test]
        public void Add_Allow_To_A_Securable_And_Deny_To_A_Role_On_The_Same_Securable()
        {
            const string securable = "r1";
            const string verb = "v1";
            const string subject = "s1";
            const string role = "roleA";

            // Act
            this.service.Allow(securable, verb, subject);
            this.service.Deny(securable, verb, role);
            var result = this.service.IsAllowed(securable, verb, subject);

            // Assert
            Assert.That(result, Is.False);
        }

        [Test]
        public void Add_Deny_To_A_Securable_And_Allow_To_A_Role_On_The_Same_Securable()
        {
            const string securable = "r1";
            const string verb = "v1";
            const string subject = "s1";
            const string role = "roleA";

            // Act
            this.service.Deny(securable, verb, subject);
            this.service.Allow(securable, verb, role);
            var result = this.service.IsAllowed(securable, verb, subject);

            // Assert
            Assert.That(result, Is.False);
        }

        [Test]
        public void Add_Deny_To_A_Securable_Parent_And_Allow_To_A_Role_On_The_Same_Securable_Parent()
        {
            const string parent = "r1";
            var securable = parent + this.service.PathDelimiter + "r1";
            const string verb = "v1";
            const string subject = "s1";
            const string role = "roleA";

            // Act
            this.service.Deny(parent, verb, subject);
            this.service.Allow(parent, verb, role);
            var result = this.service.IsAllowed(securable, verb, subject);

            // Assert
            Assert.That(result, Is.False);
        }

        [Test]
        public void Add_Allow_To_A_Securable_And_Allow_To_A_Role_On_The_Same_Securable()
        {
            const string securable = "r1";

            const string verb = "v1";
            const string subject = "s1";
            const string role = "roleA";

            // Act
            this.service.Allow(securable, verb, subject);
            this.service.Allow(securable, verb, role);
            var result = this.service.IsAllowed(securable, verb, subject);

            // Assert
            Assert.That(result, Is.True);
        }

        [Test]
        public void Add_Allow_To_A_Securable_Parent_And_Allow_To_A_Role_On_The_Same_Securable_Parent()
        {
            const string parent = "r1";
            var securable = parent + this.service.PathDelimiter + "r1";
            const string verb = "v1";
            const string subject = "s1";
            const string role = "roleA";

            // Act
            this.service.Allow(parent, verb, subject);
            this.service.Allow(parent, verb, role);
            var result = this.service.IsAllowed(securable, verb, subject);

            // Assert
            Assert.That(result, Is.True);
        }

        [Test]
        public void Add_Deny_To_Role_On_A_Securable_Parent_And_Allow_To_The_Same_Securable_Parent()
        {
            const string parent = "r1";
            var securable = parent + this.service.PathDelimiter + "r1";
            const string verb = "v1";
            const string subject = "s1";
            const string role = "roleA";

            // Act
            this.service.Deny(parent, verb, role);
            this.service.Allow(parent, verb, subject);
            var result = this.service.IsAllowed(securable, verb, subject);

            // Assert
            Assert.That(result, Is.False);
        }

        [Test]
        public void Check_Access_When_No_ACE_Added()
        {
            const string securable = "r1";
            const string verb = "v1";
            const string subject = "s1";

            // Act
            var result = this.service.IsAllowed(securable, verb, subject);

            // Assert
            Assert.That(result, Is.False);
        }
    }// ReSharper restore InconsistentNaming
}